#include <stdint.h>
#include <stdio.h>
#include <task.h>
#include <memory.h>
#include <pci_database.h>#include <pci.h>

static inline uint32_t pci_read_config_dword(const uint16_t bus, const uint16_t dev, 
                                             const uint16_t func, const uint32_t reg)
{
	out32(0xCF8, 0x80000000L |
	             ((uint32_t)bus << 16) |
	             ((uint32_t)dev << 11) |
	             ((uint32_t)func << 8) | 
                 (reg & ~3));
	return in32((int)(0xCFC + (reg & 3)));
}

static void PCI_Read_Header(uint16_t bus, uint16_t dev, uint16_t func, uint16_t id)
{
    uint32_t i;
    uint32_t location;
    location = (uint32_t)&pci_device[id];
    
    for(i = 0; i < sizeof(struct pci_device_s); i += 4)
    {
        *(uint32_t*)(location + i) = (uint32_t)pci_read_config_dword(bus, dev, func, i);
    }
}

static void PCI_Scan(void)
{
    uint16_t bus, dev, func;
    
    uint32_t vendor_id, device_id;

    for(func = 0; func < 8; func++)
    {
        for(dev = 0; dev < 32; dev++)
        {
            for(bus = 0; bus < 8; bus++)
            {
                GUTS();
                
                vendor_id = pci_read_config_dword(bus, dev, func, 0); 
                
                device_id = (uint16_t)((vendor_id >> 16) & 0xFFFF);
                vendor_id &= 0xFFFF;
                
                if(device_id != 0xFFFF)
                {
                    if(pci_devices < 256){
                    PCI_Read_Header(bus, dev, func, pci_devices);}
                    pci_devices++;
                }
            }
        }
    }
}

uint32_t find_pci_vendor(const uint16_t id)
{
    uint32_t i;
    
    for(i = 0; i < PCI_VENTABLE_COUNT; i++)
    {
        if(PciVenTable[i].VenId == id)
        {return i;}
    }
    
    return 0;
}

uint32_t find_pci_device(const uint16_t vendor, const uint16_t device)
{
    uint32_t i;
    
    for(i = 0; i < PCI_DEVTABLE_COUNT; i++)
    {
        if(PciDevTable[i].VenId == vendor && PciDevTable[i].DevId == device)
        {return i;}
    }
    
    return 0;
}

static uint32_t Bios32_Service_Directory_Check(void)
{
    uint32_t location;
    const char * sig = "_32_";
    
    for(location = 0xE0000; location < 0xFFFF0; location += 4)
    {
        if(!compare_memory((uint8_t*)location, (uint8_t*)sig, 4))
        {return *(uint32_t*)(location + 4);}
    }
    return 0;
}

static void cleanup_task(void)
{
    kill_task(active_task_id);
}

void init_pci(void)
{
    Bios32_Entry_Point = Bios32_Service_Directory_Check();
    
    pci_devices = 0;
    
    if(!Bios32_Entry_Point)
    {cleanup_task();}
    
    PCI_Scan();
    cleanup_task();
}




